#ifndef NANO_V2_H
#define NANO_V2_H

#include "../package/nano_package.h"
#include "../message/nano_message.h"
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/core/binder_common.hpp>
#include <godot_cpp/variant/packed_byte_array.hpp>
#include <godot_cpp/variant/string.hpp>



namespace godot {
class NanoV2 : public Object {
	GDCLASS(NanoV2, Object)
protected:
	static void _bind_methods();

public:
    static const int PKG_HEAD_BYTES = 4;
    static const int PKG_MAX_BYTES = 64 * 1024;
    static const int MSG_FLAG_BYTES = 1;
    static const int MSG_ROUTE_CODE_BYTES = 2;
    static const int MSG_ID_MAX_BYTES = 5;
    static const int MSG_ROUTE_LEN_BYTES = 1;
    static const int MSG_ROUTE_CODE_MAX = 0xffff;
    static const int MSG_COMPRESS_ROUTE_MASK = 0x1;
    static const int MSG_TYPE_MASK = 0x7;



	static PackedByteArray encodeStr(const String& str);
	static String decodeStr(const PackedByteArray& buffer);
	static PackedByteArray encodePackage(const Ref<NanoPackage>& p_pkg);
	static PackedByteArray encodePackageDirect(NanoPackage::PackageType type, const PackedByteArray& body);
	static Array decodePackage(const PackedByteArray& buffer);
	static PackedByteArray encodeMessage(const Ref<NanoMessage>& p_msg);
	static PackedByteArray encodeMessageDirect(NanoMessage::MessageType type, uint32_t id, bool compressRoute, 
        const String& route, const PackedByteArray& msg);
	static Ref<NanoMessage> decodeMessage(const PackedByteArray& buffer);

private:
    static bool msgHasId(NanoMessage::MessageType type);

    static bool msgHasRoute(NanoMessage::MessageType type);

    static int calculateMsgIdBytes(uint32_t id);

    static size_t encodeMsgFlag(NanoMessage::MessageType type, bool compressRoute, PackedByteArray& buffer, size_t offset);

    static size_t encodeMsgId(uint32_t id, PackedByteArray& buffer, size_t offset);

    static size_t encodeMsgRoute(bool compressRoute, const String& route, PackedByteArray& buffer, size_t offset);

    static size_t encodeMsgBody(const PackedByteArray& msg, PackedByteArray& buffer, size_t offset);
public:
    static bool judgeMessageValid(const Ref<NanoMessage>& p_msg) {
        if (!p_msg->is_valid())return false;
        if (p_msg->type < NanoMessage::TYPE_REQUEST || p_msg->type > NanoMessage::TYPE_PUSH)return false;
		if (p_msg->route.is_empty() && msgHasRoute(p_msg->type))return false;
		else if (!p_msg->route.is_empty() && !msgHasRoute(p_msg->type))return false;
		else if (!p_msg->route.is_empty() && p_msg->route.length() > MSG_ROUTE_CODE_MAX && p_msg->compress_route)return false;
		else if (!p_msg->route.is_empty() && p_msg->route.length() > 255 && !p_msg->compress_route)return false;
		if (p_msg->body.size() > PKG_MAX_BYTES)return false;
		return true;
    }
    static bool judgePackageValid(const Ref<NanoMessage>& p_pkg) {
		if (!p_pkg->is_valid())return false;
		if (p_pkg->type < NanoPackage::TYPE_HANDSHAKE || p_pkg->type > NanoPackage::TYPE_KICK)return false;
        if (p_pkg->body.size() > PKG_MAX_BYTES)return false;
        if ((p_pkg->type == NanoPackage::TYPE_HANDSHAKE || p_pkg->type == NanoPackage::TYPE_HANDSHAKE_ACK) && p_pkg->body.is_empty()) { return false; }
        if (p_pkg->type == NanoPackage::TYPE_HEARTBEAT && !p_pkg->body.is_empty()) { return false; }
		return true;
    }
};
}


#endif